﻿using Chapter1.Policies;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.ObjectPool;
using System;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Chapter1
{
    /**
     * 在Main方法中，我们构建了一个无限循环，并在每次迭代中并行执行ExecuteAsync方法三次。
     * 输出结果为：
     * Used services:1; 2; 3;
     * Used services:1; 3; 2;
     * Used services:1; 3; 2;
     * Used services:2; 3; 1;
     * Used services:1; 2; 3;
     * 
     * 可以看出每轮迭代使用的三个对象都是一样的。每次迭代，它们从对象池中被借出，使用完之后又回到池中供下一次迭代使用
     * 
     * https://www.cnblogs.com/artech/p/object-pool-01.html#4927916
     * */
    class Program
    {

        static async Task Main(string[] args)
        {
            //var objectPool = ObjectPool.Create<FoobarService>();

            object _firstItem = null;

            var val = _firstItem;
            Console.WriteLine(val == null || Interlocked.CompareExchange(ref _firstItem, null, val) != val);

            _firstItem = 1;
            val = _firstItem;
            Console.WriteLine(val == null || Interlocked.CompareExchange(ref _firstItem, null, val) != val);
            Console.WriteLine(_firstItem);


            //依赖注入
            var objectPool = new ServiceCollection().AddSingleton<ObjectPoolProvider, DefaultObjectPoolProvider>(/*p=> { return new DefaultObjectPoolProvider { MaximumRetained = 2 }; }*/)
                .BuildServiceProvider()
                .GetRequiredService<ObjectPoolProvider>()
                //.Create<FoobarService>();
                .Create(new FoobarPolicy());        //使用对象池策略

            // 默认情况下【对象池的大小】为当前机器处理器数量的2倍
            var poolSize = Environment.ProcessorCount * 2;

            while (true)
            {
                Console.Write("Used services:");
                await Task.WhenAll(Enumerable.Range(1, poolSize+1).Select(_ => ExecuteAsync()));
                Console.WriteLine();
                Console.WriteLine($"Last service: {FoobarService._lastId}");
            }

            async Task ExecuteAsync()
            {
                var service = objectPool.Get();
                try
                {
                    Console.Write($"{service.Id}; ");

                    await Task.Delay(TimeSpan.FromSeconds(1));
                }catch(Exception ex)
                {
                    Console.WriteLine(ex.Message);
                    Console.WriteLine(ex.StackTrace);
                }
                finally
                {
                    objectPool.Return(service);
                }
            }
        }
    }
}
